

module rv32is(
	input 	clock,
	input 	reset,
	output [31:0] imemaddr,
	input  [31:0] imemdataout,
	output 	imemclk,
	output [31:0] dmemaddr,
	input  [31:0] dmemdataout,
	output [31:0] dmemdatain,
	output 	dmemrdclk,
	output	dmemwrclk,
	output [2:0] dmemop,
	output	dmemwe,
	output [31:0] dbgdata,
	output m2r,
	output asrc,
	output bsrc);
//add your code here
	assign m2r = memtoreg;
	reg [31:0]pc;
	wire [31:0]next_pc;	
	
	wire [6:0]op;wire [4:0]rs1;wire [4:0]rs2; wire[4:0]rd; wire[2:0]func3; wire[6:0]func7;
	wire [2:0]extop; wire regwr; wire ALUAsrc; wire [1:0]ALUBsrc; wire [3:0]ALUctr; wire[2:0]branch;wire memtoreg;wire memwr; wire [2:0]memop;
	wire [31:0]imm_out;
	wire [31:0]busA;wire[31:0]busB;
	wire [31:0]alu_a;wire[31:0]alu_b;wire less;wire zero;wire[31:0]aluresult;	
	
	
	wire [31:0]write_back;
	
	wire PCAsrc;wire PCBsrc;
	assign asrc=PCAsrc;
	assign bsrc=PCBsrc;
	
	assign imemaddr = reset ? 0 : next_pc;
	assign imemclk = ~clock;
	assign dmemrdclk = clock;
	assign dmemwrclk = ~clock;
	assign dmemop = memop;
	assign dbgdata = pc;
	assign dmemwe = memwr;	
	assign dmemaddr = aluresult;
	assign dmemdatain = busB;

	
	assign alu_a = ALUAsrc?pc:busA;
	assign alu_b = (ALUBsrc == 2'b00)?(busB):((ALUBsrc == 2'b01)?(imm_out):(32'b100));

	assign write_back = memtoreg?dmemdataout:aluresult;
	always@(negedge clock)begin
		if(reset) begin
			pc <= 0;
		end else begin
			pc <= next_pc;		
		end
	end
pc_gen pg(.pc(pc),.imm12(imm_out),.rs1(busA),.PCAsrc(PCAsrc),.PCBsrc(PCBsrc),.next_pc(next_pc));
instr_decode in_de(.instr(imemdataout), .op(op),.rs1(rs1),.rs2(rs2),.rd(rd),.func3(func3), .func7(func7));
ctrl_gen cg(.opcode(op[6:2]),.func3(func3),.func7(func7[5]),.extop(extop),.regwr(regwr),.ALUAsrc(ALUAsrc),
.ALUBsrc(ALUBsrc),.ALUctr(ALUctr),.branch(branch),.memtoreg(memtoreg),.memwr(memwr),.memop(memop));
branch_ctrl bc(.zero(zero),.less(less),.branch(branch),.PCAsrc(PCAsrc),.PCBsrc(PCBsrc));
imm_gen imm(.instr(imemdataout), .extop(extop), .imm_out(imm_out));
regfile myregfile(.ra(rs1),.rb(rs2),.busW(rd),.wrdata(write_back),.regwr(regwr),.wrclk(~clock),.busA(busA),.busB(busB));
alu calcuMan(.dataa(alu_a),.datab(alu_b),.ALUctr(ALUctr),.less(less),.zero(zero),.aluresult(aluresult));
endmodule

module alu(input [31:0] dataa,
			  input [31:0] datab,
			  input [3:0] ALUctr,
              output less,
              output zero,
              output reg [31:0]aluresult 
	);
	wire [31:0]addsub;
	wire [31:0]shift;
	reg less_=0;
	wire cf;
	wire of;
	reg issub = 1'b0;
	reg [1:0]lral=2'b00;
	assign less = less_;
	always @(*)begin
		casex(ALUctr)
			4'b0000 : begin //add
				issub = 0;
				aluresult = addsub;
			end
			4'b1000 : begin //sub
				issub = 1;
				aluresult = addsub;
			end
			4'bx001 : begin //shl
				issub = 0;
				lral = 2'b11;
				less_ = less_;
				aluresult = shift;
			end
			4'b0010 : begin //iless
				issub=1;
				if(dataa[31]==datab[31]) begin
					if(dataa[30:0] < datab[30:0]) begin
						less_=1;
					end
					else
						less_=0;
				end else begin
					if(dataa[31] == 0) begin
						less_ = 0;
					end else begin
						less_ = 1;
					end
				end
				aluresult = less_;
			end
			4'b1010 : begin
				issub = 1;
				less_  = (dataa < datab) ;
				aluresult = (dataa < datab);
			end
			4'bx011 : begin
				issub = 0;
				aluresult = datab;
				less_ = less_;
			end
			4'bx100 : begin
				aluresult = dataa ^ datab;
				less_ = less_;
			end
			4'b0101 : begin //shr
				issub = 0;
				lral = 2'b00;
				aluresult = shift;
				less_ = less_;
			end
			4'b1101 : begin //sar
				issub = 0;
				lral = 2'b01;
				aluresult = shift;
				less_ = less_;
			end
			4'bx110 : begin
				issub = 0;
				aluresult = dataa | datab;
				less_ = less_;
			end
			4'bx111 : begin
				issub = 0;
				aluresult = dataa & datab;
				less_ = less_;
			end
		endcase
	end
	adder alu_adder(.A(dataa), .B(datab), .addsub(issub)
	, .F(addsub),.cf(cf), .zero(zero), .of(of));
	barrel shift_barrel(.indata(dataa), .shamt(datab[4:0]), 
	.lr(lral[1]), .al(lral[0]),.outdata(shift));
endmodule

module adder(
	input  [31:0] A,
	input  [31:0] B,
	input  addsub,
	output [31:0] F,
	output cf,
	output zero,
	output of
	);
	wire [31:0]b = ({32{addsub}} ^ B) + addsub;
	wire carry;
	assign {carry,F}=A + b;
	assign cf = carry ^ addsub;
	assign zero = ~(|F);		
	assign of = (b[31] == A[31])&&(F[31]!=A[31]);
endmodule

module barrel(input [31:0] indata,
			  input [4:0] shamt,
			  input lr,
			  input al,
			  output reg [31:0] outdata);

	wire [1:0]func;
	assign func = {lr, al};
	always @(*) begin
			case(func)
			2'b00: //right logical
			begin
				outdata = shamt[0] ? {1'b0, indata[31:1]} : indata;
				outdata = shamt[1] ? {2'b0, outdata[31:2]} : outdata;
				outdata = shamt[2] ? {4'b0, outdata[31:4]} : outdata;
				outdata = shamt[3] ? {8'b0, outdata[31:8]} : outdata;
				outdata = shamt[4] ? {16'b0, outdata[31:16]} : outdata;
			end
			2'b01: //right math
			begin
				outdata = shamt[0] ? {indata[31], indata[31:1]} : indata;
				outdata = shamt[1] ? {{2{outdata[31]}}, outdata[31:2]} : outdata;
				outdata = shamt[2] ? {{4{outdata[31]}}, outdata[31:4]} : outdata;
				outdata = shamt[3] ? {{8{outdata[31]}}, outdata[31:8]} : outdata;
				outdata = shamt[4] ? {{16{outdata[31]}}, outdata[31:16]} : outdata;
			end
			2'b10: //left logical
			begin
				outdata = shamt[0] ? {indata[30:0],1'b0} : indata;
				outdata = shamt[1] ? {outdata[29:0],2'b0} : outdata;
				outdata = shamt[2] ? {outdata[27:0],4'b0} : outdata;
				outdata = shamt[3] ? {outdata[23:0],8'b0} : outdata;
				outdata = shamt[4] ? {outdata[15:0],16'b0} : outdata;
			end
			2'b11: //left math
			begin

				outdata = shamt[0] ? {indata[30:0],1'b0} : indata;
				outdata = shamt[1] ? {outdata[29:0],2'b0} : outdata;
				outdata = shamt[2] ? {outdata[27:0],4'b0} : outdata;
				outdata = shamt[3] ? {outdata[23:0],8'b0} : outdata;
				outdata = shamt[4] ? {outdata[15:0],16'b0} : outdata;
			end
			endcase
	end
endmodule

module regfile(input  [4:0]  ra,
	input  [4:0]  rb,
	input  [4:0]  busW,
	input  [31:0] wrdata,
	input  regwr,
	input  wrclk,
	output [31:0] busA,
	output [31:0] busB
	);	
	
  reg [31:0]regs[31:0];
  assign busA = regs[ra];
  assign busB = regs[rb];
	
	initial begin
		regs[0]=0;
	end
  always @(posedge wrclk) begin
	if(busW!=0 && regwr==1) begin
      	regs[busW] = wrdata;
	end
	else ;
	end
endmodule

module instr_decode(input [31:0]instr, 
	output [6:0]op, output [4:0]rs1, 
	output [4:0]rs2, output [4:0]rd, output [2:0]func3,
	output [6:0]func7);
	assign op = instr[6:0];
	assign rs1 = instr[19:15];
	assign rs2 = instr[24:20];
	assign rd = instr[11:7];
	assign func3 = instr[14:12];
	assign func7 = instr[31:25];
endmodule

module ctrl_gen(input [4:0]opcode
	, input [2:0]func3
	, input func7
	, output reg [2:0]extop
	, output reg regwr
	, output reg ALUAsrc
	, output reg [1:0]ALUBsrc
	, output reg [3:0]ALUctr 
	, output reg [2:0]branch
	, output reg memtoreg
	, output reg memwr
	, output reg [2:0] memop);
	always @(*) begin
		case(opcode)
			5'b00000 : begin
				regwr <= 1'b1;
				memtoreg <= 1'b1;
				extop <= 3'b000;
				ALUAsrc <= 1'b0;
				ALUBsrc <= 2'b01;
				ALUctr <= 4'b0000;
				branch <= 3'b000;
				memwr <= 1'b0;
				memop <= func3;
			end
			5'b01000 : begin
				regwr <= 1'b0;
				memtoreg <= 1'b0;
				extop <= 3'b010;
				branch <= 3'b000;
				ALUAsrc <= 1'b0;
				ALUBsrc <= 2'b01;
				ALUctr <= 4'b0000;
				memwr <= 1'b1;
				memop <= func3;
			end
			5'b11000 : begin
				extop <= 3'b011;
				memtoreg <= 1'b0;
				regwr <= 1'b0;
				branch <= {1'b1, func3[2], func3[0]};
				ALUAsrc <= 1'b0;
				ALUBsrc <= 2'b00;
				ALUctr <= {func3[1],3'b010};
				memwr <= 1'b0;
				memop <= 3'b000;
			end
			5'b11001 : begin
				extop <= 3'b000;
				memtoreg <= 1'b0;
				regwr <= 1'b1;
				branch <= 3'b010;
				ALUAsrc <= 1'b1;
				ALUBsrc <= 2'b10;
				ALUctr <= 4'b0000;
				memwr <= 1'b0;
				memop <= 3'b000;
			end
			5'b11011 : begin
				extop <= 3'b100;
				memtoreg <= 1'b0;
				regwr <= 1'b1;
				branch <= 3'b001;
				ALUAsrc <= 1'b1;
				ALUBsrc <= 2'b10;
				ALUctr <= 4'b0000;
				memwr <= 1'b0;
				memop <= 3'b000;
			end
			5'b01100 : begin
				extop <= 3'b100;
				memtoreg <= 1'b0;
				regwr <= 1'b1;
				branch <= 3'b000;
				ALUAsrc <= 1'b0;
				ALUBsrc <= 2'b00;
				if(func3 == 3'b011)
					ALUctr <= 4'b1010;
				else
					ALUctr <= {func7,func3};
				memwr <= 1'b0;
				memop <= 3'b000;
			end
			5'b00100 : begin
				extop <= 3'b000;memtoreg <= 1'b0;
				regwr <= 1'b1;
				branch <= 3'b000;
				ALUAsrc <= 1'b0;
				ALUBsrc <= 2'b01;
				if(func3 == 3'b011)
					ALUctr <= 4'b1010;
				else if(func3 == 3'b101) begin
					ALUctr <= {func7, func3};
				end else begin
					ALUctr <= {1'b0, func3};
				end
				memwr <= 1'b0;
				memop <= 3'b000;
			end
			5'b01101 : begin
				extop <= 3'b001;
				regwr <= 1'b1;
				branch <= 3'b000;
				ALUAsrc <= 1'b0;
				ALUBsrc <= 2'b01;
				ALUctr <= 4'b0011;
				memtoreg <= 1'b0;
				memwr <= 1'b0;
				memop <= 3'b000;
			end
			default : begin
				memtoreg <= 1'b0;
				extop <= 3'b001;
				regwr <= 1'b1;
				branch <= 3'b000;
				ALUAsrc <= 1'b1;
				ALUBsrc <= 2'b01;
				ALUctr <= 4'b0000;
				memwr <= 1'b0;
				memop <= 3'b000;
			end
		endcase
	end
endmodule

module imm_gen(input [31:0]instr, input [2:0]extop, output reg [31:0]imm_out);
	wire [31:0]immI;
	wire [31:0]immU;
	wire [31:0]immS;
	wire [31:0]immB;
	wire [31:0]immJ;
	assign immI = {{20{instr[31]}}, instr[31:20]};
	assign immU = {instr[31:12], 12'b0};
	assign immS = {{20{instr[31]}}, instr[31:25], instr[11:7]};
	assign immB = {{20{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0};
	assign immJ = {{12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0};
	always @(*) begin
		case (extop) 
			3'b000 : begin
				imm_out = immI;
			end
			3'b001 : begin
				imm_out = immU;
			end
			3'b010 : begin
				imm_out = immS;
			end
			3'b011 : begin
				imm_out = immB;
			end
			3'b100 : begin
				imm_out = immJ;
			end
			default : begin
				imm_out = 32'b0;
			end
		endcase
	end
endmodule

module branch_ctrl(input zero, input less, input [2:0]branch, output reg PCAsrc, output reg PCBsrc);
	always@(*)begin
		case(branch)
			3'b000 : begin
				PCAsrc = 0;
				PCBsrc = 0;
			end
			3'b001 : begin
				PCAsrc = 1;
				PCBsrc = 0;
			end
			3'b010 : begin
				PCAsrc = 1;
				PCBsrc = 1;
			end
			3'b100 : begin
				PCAsrc = zero;
				PCBsrc = 0;
			end
			3'b101 : begin
				PCAsrc = !zero;
				PCBsrc = 0;
			end
			3'b110 : begin
				PCAsrc = less;
				PCBsrc = 0;
			end
			3'b111 : begin
				PCAsrc = !less;
				PCBsrc = 0;
			end
			default : begin
				PCAsrc = 0;
				PCBsrc = 0;
			end
		endcase
	end
endmodule

module pc_gen(input [31:0]pc, input [31:0]imm12, input [31:0]rs1, input PCAsrc, input PCBsrc, output[31:0]next_pc);
	wire [31:0]A;
	wire [31:0]B;
	wire of, zero, cf;
	assign A = PCAsrc?imm12:32'b100;
	assign B = PCBsrc?rs1:pc;
	adder pc_adder(.A(A), .B(B), .addsub(1'b0), .F(next_pc), .cf(cf), .zero(zero), .of(of));
endmodule

